﻿#region Using Statements
using System;
using GameBase.Inputs;
using GameBase.Sounds;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
#endregion

namespace GameBase.Screens
{
    /// <summary>
    /// Basic properties needed to implement any game screen.
    /// </summary>
    public abstract class Screen : IDisposable
    {
        #region Game

        /// <summary>
        /// Gets the Game that this Screen is associated to.
        /// </summary>
        public Game Game
        {
            get;
            private set;
        }

        #endregion

        #region GraphicsDevice

        /// <summary>
        /// Gets the GraphicsDevice that this Screen is associated to.
        /// </summary>
        public GraphicsDevice GraphicsDevice
        {
            get;
            private set;
        }

        #endregion

        #region SpriteBatch

        /// <summary>
        /// Gets the SpriteBatch that this Screen is associated to.
        /// </summary>
        public SpriteBatch SpriteBatch
        {
            get;
            private set;
        }

        #endregion

        #region Screen Management

        /// <summary>
        /// Gets the name of the Screen.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the ScreenManager that this Screen is associated to.
        /// </summary>
        public ScreenManager ScreenManager
        {
            get;
            private set;
        }

        /// <summary>
        /// Indicates whether any player input for this Screen is enabled.
        /// </summary>
        public bool InputEnabled
        {
            get;
            set;
        }

        /// <summary>
        /// Indicates whether this Screen is paused.
        /// </summary>
        public bool Paused
        {
            get;
            set;
        }

        #endregion

        #region Screen Transitions

        /// <summary>
        /// Makes the Screen transition to visible state.
        /// <para>Override this method with screen-specific show code.</para>
        /// </summary>
        public abstract void Show();

        /// <summary>
        /// Makes the Screen transition to hidden state.
        /// <para>Override this method with screen-specific hide code.</para>
        /// </summary>
        public abstract void Hide();

        /// <summary>
        /// Gets the time needed for a Screen to transition (in or out).
        /// </summary>
        public TimeSpan Time
        {
            get;
            private set;
        }

        #endregion

        #region Screen Dimensions

        /// <summary>
        /// Gets the width of the Screen.
        /// </summary>
        public int Width
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the height of the Screen.
        /// </summary>
        public int Height
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the aspect ratio of the Screen.
        /// </summary>
        public float AspectRatio
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the resolution scale factor associated to this Screen.
        /// </summary>
        public float Scale
        {
            get;
            private set;
        }

        #endregion

        #region Screen Content Management

        /// <summary>
        /// Gets the current per-screen ContentManager.
        /// </summary>
        public ContentManager Content
        {
            get;
            private set;
        }

        #endregion

        #region Player Input Management

        static PlayerIndex playerIndex = PlayerIndex.One;

        /// <summary>
        /// Gets the index of the player from which read input in all Screens.
        /// </summary>
        public PlayerIndex PlayerIndex
        {
            get { return playerIndex; }
            set { playerIndex = value; }
        }

        /// <summary>
        /// Gets functions for management of player input actions.
        /// </summary>
        public IPlayerInput PlayerInput
        {
            get;
            private set;
        }

        #endregion

        #region Sound Management

        /// <summary>
        /// Gets functions for management of playlist playback.
        /// </summary>
        public IPlayList PlayList
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets functions for management of sounds associated to player menu and gameplay input actions.
        /// </summary>
        public IScreenSound ScreenSound
        {
            get;
            private set;
        }

        #endregion

        #region Initialization

        /// <summary>
        /// Initializes a new instance of this class.
        /// </summary>
        /// <param name="screenManager">The ScreenManager that this Screen is associated to.</param>
        public Screen(ScreenManager screenManager)
        {
            // initialize properties
            ScreenManager = screenManager;
            Game = ScreenManager.Game;
            Time = ScreenManager.Time;

            // retrieve a shared GraphicsDevice
            GraphicsDevice = Game.GraphicsDevice;

            // retrieve a content manager from the service list
            Content = Game.Services.GetService(typeof(ContentManager)) as ContentManager;
            if (Content == null)
                throw new InvalidOperationException("Service of type ContentManager not found");

            // retrieve a shared SpriteBatch
            SpriteBatch = Game.Services.GetService(typeof(SpriteBatch)) as SpriteBatch;
            if (SpriteBatch == null)
                throw new InvalidOperationException("Service of type SpriteBatch not found");

            // retrieve a shared player input manager
            PlayerInput = Game.Services.GetService(typeof(IPlayerInput)) as IPlayerInput;
            if (PlayerInput == null)
                throw new InvalidOperationException("Service of type IPlayerInput not found");

            // retrieve a shared playlist manager
            PlayList = Game.Services.GetService(typeof(IPlayList)) as IPlayList;
            if (PlayList == null)
                throw new InvalidOperationException("Service of type IPlayList not found");

            // retrieve a shared screen sound manager
            ScreenSound = Game.Services.GetService(typeof(IScreenSound)) as IScreenSound;
            if (ScreenSound == null)
                throw new InvalidOperationException("Service of type IScreenSound not found");

            // set screen dimensions
            Width = Game.Window.ClientBounds.Width;
            Height = Game.Window.ClientBounds.Height;
            AspectRatio = GraphicsDevice.Viewport.AspectRatio;

            // set screen scale factor
            Scale = (float)Width / 1920;
        }

        /// <summary>
        /// Called when the Screen needs to be initialized.
        /// <para>Override this method with screen-specific initialize code.</para>
        /// </summary>
        public abstract void Initialize();

        #endregion

        #region Dispose

        /// <summary>
        /// Immediately releases all resources used by this object.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection.
        /// </summary>
        ~Screen()
        {
            Dispose(false);
        }

        /// <summary>
        /// Releases the unmanaged resources used by this object and optionally releases the managed resources.
        /// </summary>
        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing) { }

        #endregion

        #region Update

        /// <summary>
        /// Called when the Screen needs to be updated.
        /// <para>Override this method with screen-specific update code.</para>
        /// </summary>
        /// <param name="gameTime">Time elapsed since the last call to this method.</param>
        public abstract void Update(GameTime gameTime);

        #endregion

        #region Draw

        /// <summary>
        /// Called when the Screen needs to be drawn.
        /// <para>Override this method with screen-specific draw code.</para>
        /// </summary>
        /// <param name="gameTime">Time elapsed since the last call to this method.</param>
        public abstract void Draw(GameTime gameTime);

        #endregion
    }
}